package com.tencent.wxcloudrun.exception;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.tencent.wxcloudrun.common.Result;
import com.tencent.wxcloudrun.utils.ResultUtil;
import com.tencent.wxcloudrun.utils.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.rmi.server.ExportException;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.stream.Collectors;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public Result<String> request400(MethodArgumentTypeMismatchException e){
        log.error("HttpRequestMethodNotSupportedException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.BAD_REQUEST);
    }
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public Result<String> request405(HttpRequestMethodNotSupportedException e){
        log.error("HttpRequestMethodNotSupportedException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.METHOD_NOT_ALLOWED);
    }

    //406错误
    @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
    public Result<String> request406(HttpMediaTypeNotAcceptableException e){
        log.error("HttpMediaTypeNotAcceptableException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.NOT_ACCEPTABLE);
    }
    //500错误
    @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})
    public Result<String> server500(Exception e){
        log.error("ConversionNotSupportedException/HttpMessageNotWritableException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.INTERNAL_ERROR);
    }

    @ExceptionHandler({
            HttpMessageNotReadableException.class,
            TypeMismatchException.class,
            MissingServletRequestParameterException.class
    })
    public Result<String> handleBadRequest(Exception e) {
        log.error("Bad Request Exception ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.BAD_REQUEST);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Result<String> handleNoHandlerFoundException(NoHandlerFoundException e) {
        log.error("NoHandlerFoundException==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.NOT_FOUND, e.getHttpMethod() + " " + e.getRequestURL());
    }

    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<String> methodArgumentNotValidException(MethodArgumentNotValidException e) {
        Result<String> stringResult = extractBindExceptionErrorMessage(e);
        log.error("MethodArgumentNotValidException==>{}", stringResult.getEm(), e);
        return stringResult;
    }

    /**
     * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     */
    @ExceptionHandler(BindException.class)
    public Result<String> handleBindException(BindException e) {
        Result<String> stringResult = extractBindExceptionErrorMessage(e);
        log.error("BindException==>{}", stringResult.getEm(), e);
        return stringResult;
    }

    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是ConstraintViolationException
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<String> handleViolationException(ConstraintViolationException e) {
        log.error("ConstraintViolationException==>{}", e.getMessage(), e);
        List<String> errorList = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessageTemplate).collect(Collectors.toList());
        return ResultUtil.error(CommonErrorEnum.INVALID_PARAM, errorList.toString());
    }

    //未知方法异常
    @ExceptionHandler(NoSuchMethodException.class)
    public Result<String> noSuchMethodExceptionHandler(NoSuchMethodException e) {
        log.error("NoSuchMethodException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.UNKNOWN_METHOD_EXCEPTION);
    }

    //数组越界异常
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public Result<String> indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException e) {
        log.error("IndexOutOfBoundsException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.ARRAY_OUT_OF_BOUNDS_EXCEPTION);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BusinessException.class)
    public  Result<String> handleBusinessException(BusinessException e) {
        log.error("BusinessException ==>{}", e.getMessage(), e);
        return ResultUtil.error(e.getCode(), e.getDescribe());
    }
//
//    @ResponseStatus(HttpStatus.OK)
//    @ExceptionHandler(ServiceException.class)
//    public  Result<String> handleServiceException(ServiceException e) {
//        log.error("ServiceException ==>{}", e.getMessage(), e);
//        return ResultBuilder.error(e.getCode(), e.getDescribe());
//    }
//
//    @ExceptionHandler({BizException.class})
//    public Result<String> bizException(BizException e){
//        log.error("BizException ==>{}", e.getMessage(), e);
//        return ResultBuilder.error(e.getErrorEnum());
//    }

    @ExceptionHandler({ExportException.class})
    @ResponseStatus(HttpStatus.VARIANT_ALSO_NEGOTIATES)
    public Result<String> resultException(ExportException e){
        log.error("ExportException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.RUNTIMES_EXCEPTION);
    }

    @ExceptionHandler({DataAccessException.class})
    public Result<String> resultException(DataAccessException e){
        log.error("DataAccessException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.RUNTIMES_EXCEPTION);
    }

    @ExceptionHandler({SQLException.class})
    public Result<String> resultException(SQLException e){
        log.error("SQLException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.RUNTIMES_EXCEPTION);
    }

    //类型转换异常
    @ExceptionHandler(ClassCastException.class)
    public  Result<String> classCastExceptionHandler(ClassCastException e) {
        log.error("ClassCastException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.DATA_TYPE_CONVERSION_EXCEPTION);
    }

    //sql 唯一键值重复
    @ExceptionHandler(DuplicateKeyException.class)
    public  Result<String> duplicateKeyExceptionHandler(DuplicateKeyException e) {
        log.error("DuplicateKeyException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.DUPLICATE_KEY);

    }

    @ExceptionHandler(RejectedExecutionException.class)
    public  Result<String> rejectedExecutionExceptionHandler(RejectedExecutionException e) {
        log.error("RejectedExecutionException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.SYSTEM_BUSY);

    }

    //IO异常
    @ExceptionHandler(IOException.class)
    public  Result<String> iOExceptionHandler(IOException e) {
        log.error("IOException ==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.IO_EXCEPTION);
    }

    //空指针异常
    @ExceptionHandler(NullPointerException.class)
    public  Result<String> nullPointerExceptionHandler(NullPointerException ex) {
        log.error("空指针错误: {} - \t {}", ex.getMessage(), ex.getCause(),ex);
        return ResultUtil.error(CommonErrorEnum.NULL_VALUE_EXCEPTION);
    }

    @ExceptionHandler({RuntimeException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<String> handleRunTimeException(RuntimeException e) {
        log.error("RuntimeException==>{}", e.getMessage(), e);
        return ResultUtil.error(CommonErrorEnum.RUNTIMES_EXCEPTION, e.getMessage());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Throwable.class)
    public  Result<String> handleException(HttpServletRequest request, Throwable e) throws Throwable {
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        log.error("exception handled, request:{} detail:{}", request.getRequestURI(),
                WebUtils.describeRequest(request), e);
//        boolean useOriginalMessage = e instanceof HttpRequestMethodNotSupportedException;
//        if (!useOriginalMessage && EnvUtils.isOnline()) {
//            if (e.getMessage() != null &&
//                    e.getMessage().startsWith("Maximum upload size exceeded")) {
//                return ResultUtil.error(403, "文件太大了");
//            }
//            return ResultUtil.error(CommonErrorEnum.INTERNAL_ERROR);
//        }
        return ResultUtil.error(CommonErrorEnum.INTERNAL_ERROR.getEc(), e.getMessage());
    }

    private Result<String> extractBindExceptionErrorMessage(BindException e){
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        if (CollectionUtil.isNotEmpty(allErrors)) {
            String collect = allErrors.stream()
                    .map(error -> ((FieldError) error).getField() + StrUtil.SPACE + error.getDefaultMessage())
                    .collect(Collectors.joining("; ","[","]"));
            return ResultUtil.error(CommonErrorEnum.INVALID_PARAM,collect);
        }
        return ResultUtil.error(CommonErrorEnum.INVALID_PARAM, "");
    }
}
